<%@ page import="java.util.*"%>
<%@ page import="java.io.*"%>
<%@ page import="java.lang.reflect.*" %>

<%@taglib uri="http://java.sun.com/jstl/core" prefix="c" %>

<p>
	This tests whether the attributes held in the current HTTP session are serialisable.
	In order for web-applications to be capable of being replicated using WebLogic's in-memory clustering,
	all session attributes must be serializable.
</p>

<p>
	Therefore the object's class must implement the <code>java.io.Serializable</code> interface.
	However, simply declaring that a class implements this interface is not sufficient and can often lead
	to problems when trying to cluster a web application for the first time. This JSP can be used to help
	diagnose such problems more quickly.
</p>

<p>
	In order to guarantee that the session is truely serialisable this page tests each session attribute
 	by actually attempting to serialise it.
</p>

<br>

	<%
		boolean clusterable= true;
		int attrNum= 0;
		ByteArrayOutputStream baos = null;
		ObjectOutputStream oos = null;
		byte[] array = null;
		ByteArrayInputStream bais = null;
		ObjectInputStream ois = null;
		Object deserializedObj = null;
		String attrName= null;
		Object attrValue= null;
		String impSerStr= null;
		String doesSerStr= null;
		String pctor = null;
	    int size = 0;
	    int totalSize = 0;
	
		Enumeration attrs = pageContext.getAttributeNamesInScope (PageContext.SESSION_SCOPE);
	%>

<c:set var="sessionDetails">	
	<table>
		<tr>
			<th>#</th>
			<th>Attribute Name</th>
			<th>Class Name</th>
			<th>Serializable</th>
			<th>Serialises Okay</th>
			<th>No args C'TOR</th>
			<th>Bytes</th>
		</tr>
		<%
		while (attrs.hasMoreElements())
		{
			attrName= (String)attrs.nextElement();
		    attrValue= pageContext.getAttribute (attrName, PageContext.SESSION_SCOPE);
		    if (attrValue instanceof Serializable)
		    {
		    	impSerStr= "Yes";
		    	doesSerStr= "Yes";
		
		        pctor="<font color=\"red\"><b>No</b></font>";
		
		    	try
				{
						baos = new ByteArrayOutputStream (2048);
						oos = new ObjectOutputStream (baos);
						oos.writeObject (attrValue);
						oos.flush();
						array = baos.toByteArray();
						bais = new ByteArrayInputStream (array);
						ois = new ObjectInputStream (bais);
						deserializedObj = ois.readObject();
						
						// public constructor check
				        Constructor[] ctors = deserializedObj.getClass().getConstructors();
				        for (int i=0; i< ctors.length; i++) {
				          if ( ( (Constructor) ctors[i]).getParameterTypes().length == 0) {
				              pctor="Yes";
				          }
				        }
		
		                // flag pour detect if we found the first super class which is not serializable
		                boolean firstClassNotSerializableFound = false;
		
		                // get the first super class
		                Class superClass = deserializedObj.getClass().getSuperclass();
		                // while we didn't find the first not serializable super class
		                // or we arrive at the top level of the hierarchy
		                while (superClass!=null && !pctor.equalsIgnoreCase("Yes") && !firstClassNotSerializableFound)
		                {
		                    	// if the first not serializable super class is  Object, it is ok
		                        // because it has a public no args constructor
		                        if (superClass.getName().equals("java.lang.Object"))
		                        {
		                        	pctor = "Yes";
		                        }
		                                	  
		                    	// Test if the current super class implements the interface serializable
		                        boolean superClassIsSerializable = false;
		                        Class[] interfaces = superClass.getInterfaces();
		                        for (int k=0; k<interfaces.length; k++)
		                        {
		                        	if ("java.io.Serializable".equals(interfaces[k].getName()))
		                            {
		                            	superClassIsSerializable = true;
		                            }
		                        }
		                        
		                        // If super class not serializable and not equal to java.lang.Object
		                        if (!pctor.equalsIgnoreCase("Yes") && !superClassIsSerializable)
		                        {
		                        	// we found the first super class not serializable
		                        	firstClassNotSerializableFound = true;
		                        	
		                        	// check if there is a public constructor with no args
		                            Constructor[] sctors = superClass.getDeclaredConstructors();
		                            if (sctors!=null && sctors.length>0)
		                            {
		                            	for (int j=0; j< sctors.length; j++) {
		                                	int modifiers = ( (Constructor) sctors[j]).getModifiers();
		                                    if ( Modifier.isPublic(modifiers) && ( (Constructor) sctors[j]).getParameterTypes().length == 0) {
		                                    	pctor="Yes";
		                                    }
		                                }
		                            }
		                        }
		                        superClass = superClass.getSuperclass();
		                    }
		
		                         
		                
		                if (!pctor.equalsIgnoreCase("Yes")) {
		                  	clusterable=false;
		                }
						
						size = array.length;
						totalSize += size;
				}
				catch (NotSerializableException nse)
				{
					doesSerStr= "<font color=\"red\">No </font><em>(See stack trace on console)</em>";
					clusterable= false;
					application.log("serTest.jsp DIAG: Attribute #" + attrNum + " failed to serialise: Name=" + attrName +
						", Class=" +  attrValue.getClass().getName() + " - " + nse.getMessage() + " ...");
					nse.printStackTrace();
				}
				catch (Exception e)
				{
					doesSerStr= "<em><b>Error:</b>&nbsp;<font size=-1>" + e.getMessage() +"</font></em>";
					clusterable= false;
					application.log("serTest.jsp DIAG: Attribute #" + attrNum + " unexpected failure: Name=" + attrName +
						", Class=" +  attrValue.getClass().getName() + " - " + e.getMessage());
					e.printStackTrace();
				}
		    }
		    else
		    {
		    	clusterable= false;
		    	impSerStr= "<font color=\"red\"><b>No</b></font>";
		    	doesSerStr= "<em>n/a</em>";
		    }
		%>
			<tr>
				<td><%=attrNum++ %></td>
				<td><%=attrName%></td>
				<td><%=attrValue.getClass().getName()%></td>
				<td><%=impSerStr%></td>
				<td><%=doesSerStr%></td>
				<td><font size="-2"><%=pctor%></font></td>   
				<td><%=size%></td>
			</tr>
		
		<%
		}
		%>
	</table>

</c:set>

<h3>Session Status</h3>
<br />
<% if (clusterable) { %>
	<span class="ok">Your HTTP session supports clustering</span>
<% } else { %>
	<span class="alert">Your HTTP session does NOT support clustering</span>
<% } %>

<p>
	The total size of the objects in the HTTPSession is <strong><%= totalSize %> bytes</strong>.
</p>

<h3>Session Details</h3>
<c:out value="${sessionDetails}" escapeXml="false"></c:out>

<hr />


